package core

import (
	"github.com/garyburd/redigo/redis"
	"fmt"
	"global/constants"
	"strconv"
	"global/libs/structs"
	"strings"
	"encoding/json"
	"github.com/ngaut/log"
)

// 获取交易对K线数据
func GetAnalysisKlinePairLists(conn redis.Conn,
	exchangeName, interval, symbol string, limit int) ([][]string, error) {

	key := fmt.Sprintf(constants.AnalysisKlinePairDetailKey, exchangeName, symbol, interval)
	strs, err := redis.Strings(conn.Do("LRANGE", key, 0, limit-1))
	if err != nil {
		return nil, err
	}

	results := make([][]string, 0)
	for _, v := range strs {
		var res []string
		if err := json.Unmarshal([]byte(v), &res); err != nil {
			return nil, err
		}
		results = append(results, res)
	}
	return results, nil
}

// 获取币种列表
func GetAnalysisKlineCoinLists(conn redis.Conn, coin, interval string, length int) ([][]string, error) {
	listKey := fmt.Sprintf(constants.AnalysisKlineCoinListKey, coin, interval)
	vals, err := redis.Int64s(conn.Do("SORT", listKey, "DESC", "LIMIT", 0, length))
	if err != nil {
		return nil, err
	}

	conn.Send("MULTI")
	for _, v := range vals {
		key := fmt.Sprintf(constants.AnalysisKlineCoinDetailKey, coin, interval)
		conn.Send("HGET", key, v)
	}
	vs, err := redis.Strings(conn.Do("EXEC"))
	if err != nil {
		return nil, err
	}

	results := make([][]string, 0)
	for _, v := range vs {
		var res []string
		err := json.Unmarshal([]byte(v), &res)
		if err != nil {
			return nil, err
		}
		results = append(results, res)
	}

	return results, nil
}

// 删除
func RemoveSpiderKlineDetail(conn redis.Conn, exchangeName, interval, symbol string, t int64) error {
	key := fmt.Sprintf(constants.SpiderKlineSymbolKey, exchangeName, interval, symbol, t)
	_, err := conn.Do("DEL", key)
	return err
}

// 获取分析后的K线数据
func GetAnalysisKlineDetail(conn redis.Conn, coin string,
	intervals map[string]int64) (map[string][]string, error) {

	details := make(map[string][]string)
	for k, v := range intervals {
		key := fmt.Sprintf(constants.AnalysisKlineCoinDetailKey, coin, k)
		vals, err := redis.String(conn.Do("HGET", key, v))
		if err != nil {
			continue
		}
		var detail []string
		if err = json.Unmarshal([]byte(vals), &detail); err != nil {
			return nil, err
		}
		details[k] = detail
	}
	return details, nil
}

// 从队列获取数据
func GetKlineDataFromQueue(conn redis.Conn) (*structs.CoinKlineQueue, error) {
	data, err := redis.Values(conn.Do("BRPOP", constants.AnalysisKlineCoinQueueKey, 0))
	if err != nil {
		return nil, err
	}

	var str []string
	err = redis.ScanSlice(data, &str)
	if err != nil {
		return nil, err
	}

	if len(str) < 2 {
		return nil, fmt.Errorf("nil retuned")
	}

	var q structs.CoinKlineQueue
	if err := json.Unmarshal([]byte(str[1]), &q); err != nil {
		return nil, err
	}
	return &q, nil
}

// 保存交易对数据到分析队列
func SaveAnalysisKlineCoinToQueue(conn redis.Conn, coin string, q *structs.CoinKlineQueue) error {
	key := constants.AnalysisKlineCoinQueueKey
	str, err := json.Marshal(q)
	if err != nil {
		return err
	}
	_, err = conn.Do("LPUSH", key, string(str))
	return err
}

// 保存币种分析后的K线
func SaveAnalysisKlineCoinDetail(conn redis.Conn, coin string, datas map[string]structs.SaveKlineOption) error {
	conn.Send("MULTI")
	for k, v := range datas {
		key := fmt.Sprintf(constants.AnalysisKlineCoinDetailKey, coin, k)
		data, _ := json.Marshal(v.Data)
		if v.IsNew {
			listKey := fmt.Sprintf(constants.AnalysisKlineCoinListKey, coin, k)
			conn.Send("LPUSH", listKey, v.Data[0])
		}
		conn.Send("HSET", key, v.Data[0], string(data))
	}
	_, err := conn.Do("EXEC")
	return err
}

// 保存K线
func SaveAnalysisKlineDetail(conn redis.Conn, exchangeName, symbol string, datas map[string]structs.SaveKlineOption) error {
	conn.Send("MULTI")
	for k, v := range datas {
		key := fmt.Sprintf(constants.AnalysisKlinePairDetailKey, exchangeName, symbol, k)
		data, _ := json.Marshal(v.Data)
		if !v.IsNew {
			conn.Send("LPOP", key)
		}
		conn.Send("LPUSH", key, string(data))
	}
	_, err := conn.Do("EXEC")
	return err
}

// 获取分析后的K线最后那条记录
func GetAnalysisKlinePairLastIntervalTime(conn redis.Conn, exchangeName, symbol string,
	intervals map[string]int64) map[string][]string {
	values := make(map[string][]string, 0)
	for k := range intervals {
		key := fmt.Sprintf(constants.AnalysisKlinePairDetailKey, exchangeName, symbol, k)
		vals, err := redis.Strings(conn.Do("LRANGE", key, 0, 0))
		if err != nil || len(vals) == 0 { continue }
		resultStr := vals[0]
		var result []string
		if err := json.Unmarshal([]byte(resultStr), &result); err != nil {
			log.Errorf("err: %s", err)
		}
		values[k] = result
	}
	return values
}

// 获取抓取的第一条K线数据
func GetSpiderKlineFirstTime(conn redis.Conn, exchangeName, interval, symbol string) (int64, error) {
	key := fmt.Sprintf(constants.SpiderKlineListKey, exchangeName, interval, symbol)
	times, err := redis.Int64s(conn.Do("LRANGE", key, -1, -1))
	if err != nil {
		return 0, err
	}
	if len(times) > 0 {
		return times[0], nil
	}

	return 0, err
}

// 获取交易k线最后的一次的更新时间
func GetSpiderKlineLastTime(conn redis.Conn, exchangeName, interval, symbol string) (int, error) {
	key := fmt.Sprintf(constants.SpiderKlineLastTimeKey, exchangeName, interval, symbol)

	exists, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		return 0, err
	}
	if exists {
		lastTime, err := redis.String(conn.Do("GET", key))
		if err != nil {
			return 0, err
		}
		timeToInt, err := strconv.Atoi(lastTime)
		if err != nil {
			return 0, err
		}
		return timeToInt, nil
	}
	return 0, nil
}

// 获取spider详情
func GetSpiderKlineDetail(conn redis.Conn, exchangeName, symbol,
	interval string, timestamp int64) (*structs.SpiderKline, error) {
	detailKey := fmt.Sprintf(constants.SpiderKlineSymbolKey, exchangeName, interval, symbol, timestamp)
	vals, err := redis.Values(conn.Do("HGETALL", detailKey))
	if err != nil {
		return nil, err
	}
	var data structs.SpiderKline
	err = redis.ScanStruct(vals, &data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}

// 获取Spider获取的Kline数据
func PopTimeAndGetSpiderKlineDetail(conn redis.Conn, exchangeName, symbol, interval string) (*structs.SpiderKline, error) {

	listKey := fmt.Sprintf(constants.SpiderKlineListKey, exchangeName, interval, symbol)
	popTime, err := redis.Int64(conn.Do("RPOP", listKey))
	if err != nil {
		return nil, err
	}

	if popTime == 0 {
		return nil, fmt.Errorf("nil returned")
	}

	return GetSpiderKlineDetail(conn, exchangeName, symbol, interval, popTime)
}

// 保存K线数据
func SaveKlineDetailToSymbol(conn redis.Conn, interval string, k structs.SpiderKline, isAdd bool) error {
	conn.Send("MULTI")
	symbol := fmt.Sprintf("%s_%s", strings.ToLower(k.From), strings.ToLower(k.To))
	detailKey := fmt.Sprintf(constants.SpiderKlineSymbolKey, k.Exchange, interval, symbol, k.DateTime)

	err := conn.Send("HMSET", redis.Args{}.Add(detailKey).AddFlat(&k)...)
	if err != nil {
		return err
	}

	if isAdd {
		timeKey := fmt.Sprintf(constants.SpiderKlineLastTimeKey, k.Exchange, interval, symbol)
		err := conn.Send("SET", timeKey, k.DateTime)
		if err != nil {
			conn.Do("DISCARD")
			return err
		}

		listKey := fmt.Sprintf(constants.SpiderKlineListKey, k.Exchange, interval, symbol)
		err = conn.Send("LPUSH", listKey, k.DateTime)
		if err != nil {
			conn.Do("DISCARD")
			return err
		}
	}

	_, err = conn.Do("EXEC")
	return err
}